<!DOCTYPE html>
<html lang="cn" dir="auto">

<head><meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="robots" content="index, follow">
<title>Lua 脚本入门 | 染竹君的个人博客</title>
<meta name="keywords" content="lua">
<meta name="description" content="东拼西凑之 Lua。">
<meta name="author" content="SadBird">
<link rel="canonical" href="https://www.liyangjie.cn/posts/work/hello-lua/">
<link crossorigin="anonymous" href="/assets/css/stylesheet.min.4b7d810bc0c98044b1c7a72962d55d7f125a07c3ed5cf31e670c3fdecc98b341.css" integrity="sha256-S32BC8DJgESxx6cpYtVdfxJaB8PtXPMeZww/3syYs0E=" rel="preload stylesheet" as="style">
<link rel="icon" href="https://www.liyangjie.cn/favicon/favicon.ico">
<link rel="icon" type="image/png" sizes="16x16" href="https://www.liyangjie.cn/favicon/favicon-16x16.png">
<link rel="icon" type="image/png" sizes="32x32" href="https://www.liyangjie.cn/favicon/favicon-32x32.png">
<link rel="apple-touch-icon" href="https://www.liyangjie.cn/favicon/apple-touch-icon.png">
<link rel="mask-icon" href="https://www.liyangjie.cn/favicon/safari-pinned-tab.svg">
<meta name="theme-color" content="#2e2e33">
<meta name="msapplication-TileColor" content="#2e2e33">
<noscript>
    <style>
        #theme-toggle,
        .top-link {
            display: none;
        }

    </style>
    <style>
        @media (prefers-color-scheme: dark) {
            :root {
                --theme: rgb(29, 30, 32);
                --entry: rgb(46, 46, 51);
                --primary: rgb(218, 218, 219);
                --secondary: rgb(155, 156, 157);
                --tertiary: rgb(65, 66, 68);
                --content: rgb(196, 196, 197);
                --hljs-bg: rgb(46, 46, 51);
                --code-bg: rgb(55, 56, 62);
                --border: rgb(51, 51, 51);
            }

            .list {
                background: var(--theme);
            }

            .list:not(.dark)::-webkit-scrollbar-track {
                background: 0 0;
            }

            .list:not(.dark)::-webkit-scrollbar-thumb {
                border-color: var(--theme);
            }
        }

    </style>
</noscript>
<meta name="baidu-site-verification" content="code-9oLyeix0aK" />
<script>
    var _hmt = _hmt || [];
    (function() {
      var hm = document.createElement("script");
      hm.src = "https://hm.baidu.com/hm.js?4a41bf85d719f0e8c3165fc76904f546";
      var s = document.getElementsByTagName("script")[0]; 
      s.parentNode.insertBefore(hm, s);
    })();
</script>


<script async src="https://www.googletagmanager.com/gtag/js?id=G-C6GDZ56F4S"></script>
<script>
var doNotTrack = false;
if (!doNotTrack) {
	window.dataLayer = window.dataLayer || [];
	function gtag(){dataLayer.push(arguments);}
	gtag('js', new Date());
	gtag('config', 'G-C6GDZ56F4S', { 'anonymize_ip': false });
}
</script>
<meta property="og:title" content="Lua 脚本入门" />
<meta property="og:description" content="东拼西凑之 Lua。" />
<meta property="og:type" content="article" />
<meta property="og:url" content="https://www.liyangjie.cn/posts/work/hello-lua/" />
<meta property="og:image" content="https://s2.loli.net/2022/02/05/qse7aYBtTA9xcju.png" /><meta property="article:section" content="posts" />
<meta property="article:published_time" content="2022-02-05T22:08:07&#43;08:00" />
<meta property="article:modified_time" content="2022-02-05T22:08:07&#43;08:00" />

<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content="https://s2.loli.net/2022/02/05/qse7aYBtTA9xcju.png" />
<meta name="twitter:title" content="Lua 脚本入门"/>
<meta name="twitter:description" content="东拼西凑之 Lua。"/>


<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement": [
    {
      "@type": "ListItem",
      "position":  1 ,
      "name": "Posts",
      "item": "https://www.liyangjie.cn/posts/"
    }, 
    {
      "@type": "ListItem",
      "position":  2 ,
      "name": "Work",
      "item": "https://www.liyangjie.cn/posts/work/"
    }, 
    {
      "@type": "ListItem",
      "position":  3 ,
      "name": "Lua 脚本入门",
      "item": "https://www.liyangjie.cn/posts/work/hello-lua/"
    }
  ]
}
</script>
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "BlogPosting",
  "headline": "Lua 脚本入门",
  "name": "Lua 脚本入门",
  "description": "东拼西凑之 Lua。",
  "keywords": [
    "lua"
  ],
  "articleBody": "瞎折腾 在老笔记本上装上了 Manjaro，想整个 NeoVim 体验体验。NeoVim 内部集成了 Lua，许多插件及其配置都离不开 Lua，而且在 Redis 中也是使用 Lua 作为默认的脚本语言，到处都能看到它的身影，花点时间入个门还是挺合算的。\nHello, world 第一个程序肯定是从 Hello World 开始了，但是首先要有 Lua 的执行环境。我还是在 Windows11 上进行学习，因此直接在官网下载了二进制可执行文件，配置好环境变量就能直接用了，很简单。\n新建 hello.lua 文件，写入以下代码，保存：\n1  print('Hello, world!')   使用 lua hello.lua 就可以直接执行，在控制台输出结果。\n基本语法 注释 1 2 3 4 5 6 7 8 9 10  -- 两个 - 符号表示单行注释 -- [[ 多行注释 -- ]] -- 使用多行注释时，只要在第一个 -- 前增加一个 -，即可解除注释 --- [[ 此时已经取消注释 -- ]]   Chunk lua 中的 Chunk 表示程序段的概念，Chunk 既可以是一行简单的表达式也可以是由复杂函数、表达式共同组成的一组代码段。\n变量 标志符由字母或下划线开头（下划线通常用于有特殊作用的内部变量），后面接数字、字母或下划线。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42  -- 行末没有分号 n = 1 -- lua5.2 及其之前的版本所有数值都是 64 位的 double 双精度浮点型，lua5.3 之后新增了 64 位的 integer 整型 s = 'string' -- 支持单引号字符串 ds = \"string\" -- 支持双引号字符串 ms = [[ 支持 多行 字符串. ]] concat = s .. 'append' -- 字符串拼接使用 .. 操作符 print(concat) t = nil -- 表示空值，类似于 java 中的 null，lua 同样提供了 GC 机制 print(undefined_variable) -- 使用未声明的变量，打印 nil，而非抛出异常，与 js 类似 -- type 函数返回变量类型，支持以下几种类型 -- nil -- number -- boolean -- string -- function -- table  -- thread -- userdata print(type(n)) -- number print(type(false)) -- boolean print(undefined_variable) -- nil print(type(s)) -- string print(type(ms)) -- string print(type({})) -- table print(type(print)) -- function print(type(type)) -- thread print(type(io.stdin)) -- userdata -- 局部变量 local h -- 多变量赋值 a, b = 2, 3   逻辑运算符 boolean 类型的值只有两个：true 和 false。但在 lua 中任何值都可以表示条件的真假。在 lua 语言中，条件测试（如 if 条件判断）将所有除了 false 和 nil 以外的值都视为 true，包括 0、空串、{} 等。\n常用逻辑运算符包括：and、or 和 not，等值判断使用 == 符号，不等判断使用 ~=。and 表示如果第一个操作数为 false，则返回第一个操作数；否则，返回第二个操作数。or 则表示如果第一个操作数为 true，则返回第一个操作数，否则返回第二个操作数。and 和 or 运算符都有短路的含义，只在特定条件下会触发第二个操作数的求值。not 返回值为 boolean 类型，操作数为 false 和 nil 时返回 true，其余情况下，not 返回值均为 false。\n在 lua 中，or 有一个常见的用法：x = x or y，这个表达相当于：\n1  if not x then x = v end   表示当 x 未初始化时，将其初始化为 y。\ntable table 是 lua 语言中最为强大的类型，使用 talbe 可以表达我们常用的数组、字典等数据结构，可以类比到 js 中的 object。\ntable 最简单的创建方式如下：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33  v1 = 1 v2 = false v3 = \"lua\" t = { [\"k1\"] = v1, [\"k2\"] = v2, [\"k3\"] = v3 } print(t[\"k1\"]) print(t[\"k2\"]) print(t[\"k3\"]) -- 由于 k1、k2、k3 为简单字符串，lua 提供了以下简写 print(t.k1) print(t.k2) print(t.k3) -- 索引 key 不仅仅可以是字符串，还可以是数字，甚至是表类型 t[1] = 100 t[{}] = \"haha\" t[2.0] = \"two\" print(t[2]) -- 打印 two，对于数值类型的索引 key，最终都会被转换为整型，如 2.0 - 2 -- 以下对应两个不同的索引，不能混为一谈 t[\"10\"] = \"10\" t[10] = 20 -- for 遍历 for k, v in pairs(t) do print(k, v) end   没有显式索引的情况下，table 可以作为数组（或称为序列）使用：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39  a = {\"Monday\", \"Tuesday\", \"Wednesday\", \"Thurday\", \"Friday\", \"Saturday\", \"Sunday\"} -- 使用表的遍历方式，从结果中可以看出 index 索引从 1 开始 for index, v in ipairs(a) do print(index, v) end -- 第二种遍历方式，for 的步进形式，#a 表示 table 的长度 for k = 1, #a do print(k, a[k]) end -- 步进默认为 1，调整为 2 for i = 1, #a, 2 do print(i, a[i]) end -- 注意 # 的使用，它一般用来获取 table 获 string 的长度（字节长度），在表末尾有 nil 时，末尾的 nil 不计入长度 print(#a) -- 返回 a 的长度 b = {10, 20, 30, nil, 50} print(#b) -- 5，nil 不在末尾的情况 c = {10, 20, 30, 40, nil, nil} print(#c) -- 4，末尾 nil 不计入总长度 -- 获取最后一个元素 last = a[#a] -- 删除最后一个元素 a[#a] = nil -- table 标准库 api q = {10, 20, 30, 40} table.insert(q, 1, 5) -- {5, 10, 20, 30, 40} table.insert(q, 50) -- 不指定插入位置，默认在末尾插入，{5, 10, 20, 30, 40, 50} table.remove(q, 2) -- 删除指定位置元素，并将后续元素往前移动，{5, 20, 30, 40, 50} p = {} table.move(q, 2, 4, 1, p) -- 将 q 中 2-4 位置处的元素拷贝到 p 中的 1 位置，若省略 p 参数，则是在 q 表内部移动   Function lua 的 function 可以直接类比 js 中的 function，它属于第一类值（first-class value）。这就意味着它与其他常见类型的值（如数字、字符串等）具有相同的权限，可以将 function 保存在变量或表中，也可以将 function 作为参数传递给其他 function。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44  -- function 的基本定义和使用 function myprint(a, b) print(a, b) end myprint(a,b) -- 参数个数不匹配时 myprint() -- nil nil myprint(1) -- 1 nil myprint(1, 2, 3) -- 1 2（3 被忽略） -- 多返回值 function maximun(a) local max_index = 1 local max_value = a[max_index] for i = 1, #a do if a[i]  max_value then max_index = i max_value = a[i] end end return max_index, max_value end j, k = maximun({6, 9, 4, 15, 7, 20, 10}) print(j, k) -- 6 20 -- 可变长参数 function add(...) local sum = 0 -- 使用 {...} 的方式可以将变长参数转换为 table for _, v in ipairs({...}) do sum = sum + v end return sum end print(add(1,2,3)) -- 6 print(add(1,2,3,4,5)) -- 15   扩展内容 闭包 同样与 JS 进行类比，上述对于 lua 函数的定义方式只是 lua 中提供的语法糖，本质上，函数的定义如下：\n1 2 3 4 5  function func() print('hello world') end -- 等价于 func = function() print('hello world') end   lua 函数的这种特性，可以将函数赋值给全局变量、局部变量或者表中的值。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30  a = {} a.p = print -- 将 print 函数赋值给表 a 的 p 变量 a.p('hello world') -- 等同于 print('hello world') -- 非全局函数 -- 以下几种方式等价 foo = {} foo.bar = function(x) return 2 * x end foo = { bar: function(x) return 2 * x end } foo = {} function foo.bar(x) return 2 * x end -- 将函数声明为局部函数 local function foo(x) return 2 * x end -- 局部函数的声明问题 local function fib(n) if n  2 then return n end return fib(n - 1) + fib(n - 2) -- 此处有问题，fib 此时不可见 end -- 需要写成以下方式 local fib fib = function(n) if n  2 then return n end return fib(n - 1) + fib(n - 2) -- 解决上述问题 end   闭包是个比较难理解的概念，《Programming in Lua》中描述如下：\nClosure\r\rSimply put, a closure is a function plus all it needs to access its upvalues correctly.\r\r 这里又要引入 upvalue（上值）的概念，示例如下：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15  function newCounter() local counter = 0 -- 既不是局部变量，也不是全局变量，在 lua 中称之为 upvalue return function() counter = counter + 1 return counter end end c1 = newCounter() print(c1()) -- 1 print(c1()) -- 2 c2 = newCounter() -- 新增计数器，创建了新的环境，不同于 c1，因此重新计数 print(c2()) -- 1 print(c2()) -- 2   上述代码中，newCounter 函数中定义了一个 counter 变量，它与普通局部变量最大的不同之处在于，当执行完 c1 = newCounter() 这句代码后，其实已经超出了 counter 的作用域，但是从后续 c1() 的两次调用中我们可以看到，仍然能够正常访问到 counter 的值，在 lua 中，类似 counter 的这种变量都被称为 upvalue（上值）。\n闭包就是 newCounter 的匿名返回函数与该返回函数所在的一个上下文环境的统称（counter 就在该上下文环境中）。换句话说，实际上newCounter 返回的不仅仅是匿名函数本身，还包括了一个包含该匿名函数的上下文环境，环境中定义了一系列的 upvalue（若 newCounter 方法有参数，这些参数也属于 upvalue）。\n闭包的应用场景较多，典型的一个应用是 GUI 交互的回调上，假设要做一个计算器的应用，需要 0-9 的数字按键，每个按键点击后需要在显示区域显示，因此可以定义如下函数：\n1 2 3 4 5 6 7  function digitButton (digit) return Button{ label = digit, action = function () add_to_display(digit) end } end   其中，digit 即为按键上的数值，这样每个按钮对应的数值都不相同，通过使用闭包，可以将所有按钮的逻辑统一。\nmetatable metatable 提供了对 table 的操作重载功能，比如现在有两个 table 分别代表两个分数，如下：\n1 2  f1 = {a = 1, b = 2} -- 表示 1/2 f2 = {a = 2, b = 3} -- 表示 2/3   现在想要定义一个加法操作，表示两个分数相加的逻辑，即 result = f1 + f2，使用 metatalbe 如下：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15  metafraction = {} -- __add 是 lua 规定的 metamethod 之一，表示 + 号的重载 function metafraction.__add(f1, f2) sum = {} sum.b = f1.b * f2.b sum.a = f1.a * f2.b + f2.a * f1.b return sum end -- 设置 metatable，该 table 重载了 _add，为 f1 和 f2 提供了 + 号的重载操作 setmetatable(f1, metafraction) setmetatable(f2, metafraction) -- 直接使用 + 号进行分数的求和计算 s = f1 + f2   除了 _add，lua 还提供了许多可供选择的操作：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15  -- __add(a, b) for a + b -- __sub(a, b) for a - b -- __mul(a, b) for a * b -- __div(a, b) for a / b -- __mod(a, b) for a % b -- __pow(a, b) for a ^ b -- __unm(a) for -a -- __concat(a, b) for a .. b -- __len(a) for #a -- __eq(a, b) for a == b -- __lt(a, b) for a -- __le(a, b) for a -- __index(a, b) for a.b -- __newindex(a, b, c) for a.b = c -- __call(a, ...) for a(...)   模块化 我们自定义一个 mod.lua 文件作为我们的模块，文件内容如下：\n1 2 3 4 5 6 7 8 9 10 11 12  local M = {} local function sayMyName() print('Hrunkner') end function M.sayHello() print('Why hello there') sayMyName() end return M   如果需要在另一个文件中引入该模块，则需要使用 require：\n1 2 3 4 5 6 7 8 9 10  local mod = require('mod') -- 注意，没有.lua 后缀 mod.sayHello() mod.sayMyName() -- require 的作用可近似看为立即执行函数，但它有缓存机制，require 同一个模块只会执行一次 local mod = (function() --  end)()   面向对象 lua 中没有提供内置的 Class 机制，但可以通过 table 和 metatable 实现面向对象的语义，其中 __index 的语义表示访问操作符 . 的重载（例如 table a 的 metatable 中设定了 __index 为 b，那么当使用 a.attr 访问 attr 时，会先在 table a 中查找，若 a 中没有，则会去 b 中查询 attr）：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35  -- Dog 类本质上是一个 table Dog = {} -- function t:fn(...) 是一个语法糖，相当于 function t.fn(self, ...)，只是在函数中添加了一个 self 参数 function Dog:new() -- 每次 new 生成一个新的 table newDog = {sound = \"wang wang！\"} -- 将第一个参数 self 的 __index 设定为 self 本身，表示 newDog 后续的查询范围是在本表内部 self.__index = self -- setmetatable 返回设定好 metatable 的 newDog return setmetatable(newDog, self) end function Dog:makeSound() print('I say' .. self.sound) end -- 相当于 dogInstance = Dog.new(Dog)，相当于将新表 newDog 的 metatable 赋值为 Dog，并返回 newDog dogInstance = Dog:new() -- 相当于 newDog.makesound(newDog)，此时，makeSound 存在于 Dog 表中，由于设定了 metatable 的 __index，所以 在 newDog 中能访问到，且传入的 self 为 newDog,即 self = newDog dogInstance:makeSound() -- 类的继承 -- LoudDog = Dog.new(Dog)，因此 self 为 Dog，即 LoudDog 的 metatable.__index 为 Dog LoudDog = Dog:new() -- 重新定义 makeSound() 函数，注意这个函数此时是在 LoudDog 表中 function LoudDog:makeSound() s = self.sound .. ' ' print(s .. s .. s) end -- LoudDog.new(LoudDog)，self 为 LoudDog，因此 seymour 的 metatable.__index 为 LoudDog seymour = LoudDog:new() -- seymour.makeSound(seymour)，由于 seymour 的 metatable.__index 为 LoudDog，因此调用的是 LoudDog 中的 makeSound，实现了覆盖 seymour:makeSound() -- 'woof woof woof'    ",
  "wordCount" : "1621",
  "inLanguage": "cn",
  "image":"https://s2.loli.net/2022/02/05/qse7aYBtTA9xcju.png","datePublished": "2022-02-05T22:08:07+08:00",
  "dateModified": "2022-02-05T22:08:07+08:00",
  "author":[{
    "@type": "Person",
    "name": "SadBird"
  }],
  "mainEntityOfPage": {
    "@type": "WebPage",
    "@id": "https://www.liyangjie.cn/posts/work/hello-lua/"
  },
  "publisher": {
    "@type": "Organization",
    "name": "染竹君的个人博客",
    "logo": {
      "@type": "ImageObject",
      "url": "https://www.liyangjie.cn/favicon/favicon.ico"
    }
  }
}
</script>
</head>

<body class="">
    <a id="top"></a>
<script>
    if (localStorage.getItem("pref-theme") === "dark") {
        document.body.classList.add('dark');
    } else if (localStorage.getItem("pref-theme") === "light") {
        document.body.classList.remove('dark')
    } else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
        document.body.classList.add('dark');
    }

</script>

<header class="header">
    <nav class="nav">
        <div class="logo">
            <a href="https://www.liyangjie.cn/" accesskey="h" title="染竹君的小站 (Alt + H)">
                        
                    <img src="https://www.liyangjie.cn/img/logo_hue7837a3d2f79ccdd94da0537755daebc_15738_0x30_resize_box_3.png" alt="logo" aria-label="logo"
                        height="30">染竹君的小站</a>
            <span class="logo-switches">
                <button id="theme-toggle" accesskey="t" title="(Alt + T)">
                    <svg id="moon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                        fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                        stroke-linejoin="round">
                        <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
                    </svg>
                    <svg id="sun" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"
                        fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                        stroke-linejoin="round">
                        <circle cx="12" cy="12" r="5"></circle>
                        <line x1="12" y1="1" x2="12" y2="3"></line>
                        <line x1="12" y1="21" x2="12" y2="23"></line>
                        <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
                        <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
                        <line x1="1" y1="12" x2="3" y2="12"></line>
                        <line x1="21" y1="12" x2="23" y2="12"></line>
                        <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
                        <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
                    </svg>
                </button>
            </span>
        </div>
        <ul id="menu">
            <li>
                <a href="https://www.liyangjie.cn/categories/" title="分类">
                    <span>分类</span>
                </a>
            </li>
            <li>
                <a href="https://www.liyangjie.cn/tags/" title="标签">
                    <span>标签</span>
                </a>
            </li>
            <li>
                <a href="https://www.liyangjie.cn/archives/" title="归档">
                    <span>归档</span>
                </a>
            </li>
            <li>
                <a href="https://www.liyangjie.cn/search/" title="搜索">
                    <span>搜索</span>
                </a>
            </li>
        </ul>
    </nav>
</header>
<main class="main">

<article class="post-single">
  <header class="post-header">
    <div class="breadcrumbs"><a href="https://www.liyangjie.cn/">Home</a>&nbsp;»&nbsp;<a href="https://www.liyangjie.cn/posts/">Posts</a>&nbsp;»&nbsp;<a href="https://www.liyangjie.cn/posts/work/">Work</a></div>
    <h1 class="post-title">
      Lua 脚本入门
    </h1>
    <div class="post-meta"><span title='2022-02-05 22:08:07 +0800 CST'>February 5, 2022</span>&nbsp;·&nbsp;8 min&nbsp;·&nbsp;SadBird&nbsp;|&nbsp;<a href="https://github.com/YazidLee/hugo-backup/tree/master/content/posts/work/Lua%20%e8%84%9a%e6%9c%ac%e5%85%a5%e9%97%a8.md" rel="noopener noreferrer" target="_blank">Suggest Changes</a>
</div>
  </header> <figure class="entry-cover"><img class="lazy" src="/svg/loading.min.svg"  data-src="https://s2.loli.net/2022/02/05/qse7aYBtTA9xcju.png" loading="lazy" alt="">
        
</figure><div class="toc">
    <details >
        <summary accesskey="c" title="(Alt + C)">
            <span class="details">Table of Contents</span>
        </summary>

        <div class="inner"><ul>
                <li>
                    <a href="#%e7%9e%8e%e6%8a%98%e8%85%be" aria-label="瞎折腾">瞎折腾</a></li>
                <li>
                    <a href="#hello-world" aria-label="Hello, world">Hello, world</a></li>
                <li>
                    <a href="#%e5%9f%ba%e6%9c%ac%e8%af%ad%e6%b3%95" aria-label="基本语法">基本语法</a><ul>
                        
                <li>
                    <a href="#%e6%b3%a8%e9%87%8a" aria-label="注释">注释</a></li>
                <li>
                    <a href="#chunk" aria-label="Chunk">Chunk</a></li>
                <li>
                    <a href="#%e5%8f%98%e9%87%8f" aria-label="变量">变量</a></li>
                <li>
                    <a href="#%e9%80%bb%e8%be%91%e8%bf%90%e7%ae%97%e7%ac%a6" aria-label="逻辑运算符">逻辑运算符</a></li>
                <li>
                    <a href="#table" aria-label="table">table</a></li>
                <li>
                    <a href="#function" aria-label="Function">Function</a></li></ul>
                </li>
                <li>
                    <a href="#%e6%89%a9%e5%b1%95%e5%86%85%e5%ae%b9" aria-label="扩展内容">扩展内容</a><ul>
                        
                <li>
                    <a href="#%e9%97%ad%e5%8c%85" aria-label="闭包">闭包</a></li>
                <li>
                    <a href="#metatable" aria-label="metatable">metatable</a></li>
                <li>
                    <a href="#%e6%a8%a1%e5%9d%97%e5%8c%96" aria-label="模块化">模块化</a></li>
                <li>
                    <a href="#%e9%9d%a2%e5%90%91%e5%af%b9%e8%b1%a1" aria-label="面向对象">面向对象</a>
                </li>
            </ul>
            </li>
            </ul>
        </div>
    </details>
</div>

  <div class="post-content"><h2 id="瞎折腾">瞎折腾<a hidden class="anchor" aria-hidden="true" href="#瞎折腾">#</a></h2>
<p>在老笔记本上装上了 Manjaro，想整个 NeoVim 体验体验。NeoVim 内部集成了 Lua，许多插件及其配置都离不开 Lua，而且在 Redis 中也是使用 Lua 作为默认的脚本语言，到处都能看到它的身影，花点时间入个门还是挺合算的。</p>
<h2 id="hello-world">Hello, world<a hidden class="anchor" aria-hidden="true" href="#hello-world">#</a></h2>
<p>第一个程序肯定是从 Hello World 开始了，但是首先要有 Lua 的执行环境。我还是在 Windows11 上进行学习，因此直接在<a href="http://luabinaries.sourceforge.net/">官网</a>下载了二进制可执行文件，配置好环境变量就能直接用了，很简单。</p>
<p>新建 <code>hello.lua</code> 文件，写入以下代码，保存：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="s1">&#39;Hello, world!&#39;</span><span class="p">)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>使用 <code>lua hello.lua</code> 就可以直接执行，在控制台输出结果。</p>
<h2 id="基本语法">基本语法<a hidden class="anchor" aria-hidden="true" href="#基本语法">#</a></h2>
<h3 id="注释">注释<a hidden class="anchor" aria-hidden="true" href="#注释">#</a></h3>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="c1">-- 两个 - 符号表示单行注释</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- [[</span>
</span></span><span class="line"><span class="cl">  <span class="err">多行注释</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- ]]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- 使用多行注释时，只要在第一个 -- 前增加一个 -，即可解除注释</span>
</span></span><span class="line"><span class="cl"><span class="c1">--- [[</span>
</span></span><span class="line"><span class="cl">  <span class="err">此时已经取消注释</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- ]]</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="chunk">Chunk<a hidden class="anchor" aria-hidden="true" href="#chunk">#</a></h3>
<p>lua 中的 Chunk 表示<strong>程序段</strong>的概念，Chunk 既可以是一行简单的表达式也可以是由复杂函数、表达式共同组成的一组代码段。</p>
<h3 id="变量">变量<a hidden class="anchor" aria-hidden="true" href="#变量">#</a></h3>
<p>标志符由字母或下划线开头（下划线通常用于有特殊作用的内部变量），后面接数字、字母或下划线。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="c1">-- 行末没有分号</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">n</span> <span class="o">=</span> <span class="mi">1</span> <span class="c1">-- lua5.2 及其之前的版本所有数值都是 64 位的 double 双精度浮点型，lua5.3 之后新增了 64 位的 integer 整型</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">s</span> <span class="o">=</span> <span class="s1">&#39;string&#39;</span> <span class="c1">-- 支持单引号字符串</span>
</span></span><span class="line"><span class="cl"><span class="n">ds</span> <span class="o">=</span> <span class="s2">&#34;string&#34;</span> <span class="c1">-- 支持双引号字符串</span>
</span></span><span class="line"><span class="cl"><span class="n">ms</span> <span class="o">=</span> <span class="s">[[
</span></span></span><span class="line"><span class="cl"><span class="s">    支持
</span></span></span><span class="line"><span class="cl"><span class="s">    多行
</span></span></span><span class="line"><span class="cl"><span class="s">    字符串.
</span></span></span><span class="line"><span class="cl"><span class="s">]]</span>
</span></span><span class="line"><span class="cl"><span class="n">concat</span> <span class="o">=</span> <span class="n">s</span> <span class="o">..</span> <span class="s1">&#39;append&#39;</span> <span class="c1">-- 字符串拼接使用 .. 操作符</span>
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="n">concat</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">t</span> <span class="o">=</span> <span class="kc">nil</span> <span class="c1">-- 表示空值，类似于 java 中的 null，lua 同样提供了 GC 机制</span>
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="n">undefined_variable</span><span class="p">)</span> <span class="c1">-- 使用未声明的变量，打印 nil，而非抛出异常，与 js 类似</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- type 函数返回变量类型，支持以下几种类型</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- nil</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- number</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- boolean</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- string</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- function</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- table </span>
</span></span><span class="line"><span class="cl"><span class="c1">-- thread</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- userdata</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="n">type</span><span class="p">(</span><span class="n">n</span><span class="p">))</span> <span class="c1">-- number</span>
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="n">type</span><span class="p">(</span><span class="kc">false</span><span class="p">))</span> <span class="c1">-- boolean</span>
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="n">undefined_variable</span><span class="p">)</span> <span class="c1">-- nil</span>
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="n">type</span><span class="p">(</span><span class="n">s</span><span class="p">))</span> <span class="c1">-- string</span>
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="n">type</span><span class="p">(</span><span class="n">ms</span><span class="p">))</span> <span class="c1">-- string</span>
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="n">type</span><span class="p">({}))</span> <span class="c1">-- table</span>
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="n">type</span><span class="p">(</span><span class="n">print</span><span class="p">))</span> <span class="c1">-- function</span>
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="n">type</span><span class="p">(</span><span class="n">type</span><span class="p">))</span> <span class="c1">-- thread</span>
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="n">type</span><span class="p">(</span><span class="n">io.stdin</span><span class="p">))</span> <span class="c1">-- userdata</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- 局部变量</span>
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">h</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- 多变量赋值</span>
</span></span><span class="line"><span class="cl"><span class="n">a</span><span class="p">,</span> <span class="n">b</span> <span class="o">=</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="逻辑运算符">逻辑运算符<a hidden class="anchor" aria-hidden="true" href="#逻辑运算符">#</a></h3>
<p>boolean 类型的值只有两个：true 和 false。但在 lua 中任何值都可以表示条件的真假。在 lua 语言中，条件测试（如 if 条件判断）将所有除了 false 和 nil 以外的值都视为 true，包括 0、空串、{} 等。</p>
<p>常用逻辑运算符包括：and、or 和 not，等值判断使用 <code>==</code> 符号，不等判断使用 <code>~=</code>。and 表示如果第一个操作数为 false，则返回第一个操作数；否则，返回第二个操作数。or 则表示如果第一个操作数为 true，则返回第一个操作数，否则返回第二个操作数。and 和 or 运算符都有短路的含义，只在特定条件下会触发第二个操作数的求值。not 返回值为 boolean 类型，操作数为 false 和 nil 时返回 true，其余情况下，not 返回值均为 false。</p>
<p>在 lua 中，or 有一个常见的用法：<code>x = x or y</code>，这个表达相当于：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kr">if</span> <span class="ow">not</span> <span class="n">x</span> <span class="kr">then</span> <span class="n">x</span> <span class="o">=</span> <span class="n">v</span> <span class="kr">end</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>表示当 x 未初始化时，将其初始化为 y。</p>
<h3 id="table">table<a hidden class="anchor" aria-hidden="true" href="#table">#</a></h3>
<p>table 是 lua 语言中最为强大的类型，使用 talbe 可以表达我们常用的数组、字典等数据结构，可以类比到 js 中的 object。</p>
<p>table 最简单的创建方式如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="n">v1</span> <span class="o">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="n">v2</span> <span class="o">=</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl"><span class="n">v3</span> <span class="o">=</span> <span class="s2">&#34;lua&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">t</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="s2">&#34;k1&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="n">v1</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="s2">&#34;k2&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="n">v2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="s2">&#34;k3&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="n">v3</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="n">t</span><span class="p">[</span><span class="s2">&#34;k1&#34;</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="n">t</span><span class="p">[</span><span class="s2">&#34;k2&#34;</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="n">t</span><span class="p">[</span><span class="s2">&#34;k3&#34;</span><span class="p">])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- 由于 k1、k2、k3 为简单字符串，lua 提供了以下简写</span>
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="n">t.k1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="n">t.k2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="n">t.k3</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- 索引 key 不仅仅可以是字符串，还可以是数字，甚至是表类型</span>
</span></span><span class="line"><span class="cl"><span class="n">t</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="mi">100</span>
</span></span><span class="line"><span class="cl"><span class="n">t</span><span class="p">[{}]</span> <span class="o">=</span> <span class="s2">&#34;haha&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">t</span><span class="p">[</span><span class="mf">2.0</span><span class="p">]</span> <span class="o">=</span> <span class="s2">&#34;two&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="n">t</span><span class="p">[</span><span class="mi">2</span><span class="p">])</span> <span class="c1">-- 打印 two，对于数值类型的索引 key，最终都会被转换为整型，如 2.0 -&gt; 2</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- 以下对应两个不同的索引，不能混为一谈</span>
</span></span><span class="line"><span class="cl"><span class="n">t</span><span class="p">[</span><span class="s2">&#34;10&#34;</span><span class="p">]</span> <span class="o">=</span> <span class="s2">&#34;10&#34;</span>
</span></span><span class="line"><span class="cl"><span class="n">t</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span> <span class="o">=</span> <span class="mi">20</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- for 遍历</span>
</span></span><span class="line"><span class="cl"><span class="kr">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="kr">in</span> <span class="n">pairs</span><span class="p">(</span><span class="n">t</span><span class="p">)</span> <span class="kr">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">print</span><span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>没有显式索引的情况下，table 可以作为数组（或称为序列）使用：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="n">a</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&#34;Monday&#34;</span><span class="p">,</span> <span class="s2">&#34;Tuesday&#34;</span><span class="p">,</span> <span class="s2">&#34;Wednesday&#34;</span><span class="p">,</span> <span class="s2">&#34;Thurday&#34;</span><span class="p">,</span> <span class="s2">&#34;Friday&#34;</span><span class="p">,</span> <span class="s2">&#34;Saturday&#34;</span><span class="p">,</span> <span class="s2">&#34;Sunday&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- 使用表的遍历方式，从结果中可以看出 index 索引从 1 开始</span>
</span></span><span class="line"><span class="cl"><span class="kr">for</span> <span class="n">index</span><span class="p">,</span> <span class="n">v</span> <span class="kr">in</span> <span class="n">ipairs</span><span class="p">(</span><span class="n">a</span><span class="p">)</span> <span class="kr">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">print</span><span class="p">(</span><span class="n">index</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- 第二种遍历方式，for 的步进形式，#a 表示 table 的长度</span>
</span></span><span class="line"><span class="cl"><span class="kr">for</span> <span class="n">k</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="o">#</span><span class="n">a</span> <span class="kr">do</span>
</span></span><span class="line"><span class="cl">  <span class="n">print</span><span class="p">(</span><span class="n">k</span><span class="p">,</span> <span class="n">a</span><span class="p">[</span><span class="n">k</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- 步进默认为 1，调整为 2</span>
</span></span><span class="line"><span class="cl"><span class="kr">for</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="o">#</span><span class="n">a</span><span class="p">,</span> <span class="mi">2</span> <span class="kr">do</span> 
</span></span><span class="line"><span class="cl">  <span class="n">print</span><span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- 注意 # 的使用，它一般用来获取 table 获 string 的长度（字节长度），在表末尾有 nil 时，末尾的 nil 不计入长度</span>
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="o">#</span><span class="n">a</span><span class="p">)</span> <span class="c1">-- 返回 a 的长度</span>
</span></span><span class="line"><span class="cl"><span class="n">b</span> <span class="o">=</span> <span class="p">{</span><span class="mi">10</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="kc">nil</span><span class="p">,</span> <span class="mi">50</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="o">#</span><span class="n">b</span><span class="p">)</span> <span class="c1">-- 5，nil 不在末尾的情况</span>
</span></span><span class="line"><span class="cl"><span class="n">c</span> <span class="o">=</span> <span class="p">{</span><span class="mi">10</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="mi">40</span><span class="p">,</span> <span class="kc">nil</span><span class="p">,</span> <span class="kc">nil</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="o">#</span><span class="n">c</span><span class="p">)</span> <span class="c1">-- 4，末尾 nil 不计入总长度</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- 获取最后一个元素</span>
</span></span><span class="line"><span class="cl"><span class="n">last</span> <span class="o">=</span> <span class="n">a</span><span class="p">[</span><span class="o">#</span><span class="n">a</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- 删除最后一个元素</span>
</span></span><span class="line"><span class="cl"><span class="n">a</span><span class="p">[</span><span class="o">#</span><span class="n">a</span><span class="p">]</span> <span class="o">=</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- table 标准库 api</span>
</span></span><span class="line"><span class="cl"><span class="n">q</span> <span class="o">=</span> <span class="p">{</span><span class="mi">10</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="mi">40</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="n">table.insert</span><span class="p">(</span><span class="n">q</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">5</span><span class="p">)</span> <span class="c1">-- {5, 10, 20, 30, 40}</span>
</span></span><span class="line"><span class="cl"><span class="n">table.insert</span><span class="p">(</span><span class="n">q</span><span class="p">,</span> <span class="mi">50</span><span class="p">)</span> <span class="c1">-- 不指定插入位置，默认在末尾插入，{5, 10, 20, 30, 40, 50}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">table.remove</span><span class="p">(</span><span class="n">q</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> <span class="c1">-- 删除指定位置元素，并将后续元素往前移动，{5, 20, 30, 40, 50}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">p</span> <span class="o">=</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="n">table.move</span><span class="p">(</span><span class="n">q</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">p</span><span class="p">)</span> <span class="c1">-- 将 q 中 2-4 位置处的元素拷贝到 p 中的 1 位置，若省略 p 参数，则是在 q 表内部移动</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="function">Function<a hidden class="anchor" aria-hidden="true" href="#function">#</a></h3>
<p>lua 的 function 可以直接类比 js 中的 function，它属于<strong>第一类值（<em>first-class value</em>）</strong>。这就意味着它与其他常见类型的值（如数字、字符串等）具有相同的权限，可以将 function 保存在变量或表中，也可以将 function 作为参数传递给其他 function。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="c1">-- function 的基本定义和使用</span>
</span></span><span class="line"><span class="cl"><span class="kr">function</span> <span class="nf">myprint</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span> 
</span></span><span class="line"><span class="cl">  <span class="n">print</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">myprint</span><span class="p">(</span><span class="n">a</span><span class="p">,</span><span class="n">b</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- 参数个数不匹配时</span>
</span></span><span class="line"><span class="cl"><span class="n">myprint</span><span class="p">()</span> <span class="c1">-- nil    nil</span>
</span></span><span class="line"><span class="cl"><span class="n">myprint</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="c1">-- 1    nil</span>
</span></span><span class="line"><span class="cl"><span class="n">myprint</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span> <span class="c1">-- 1    2（3 被忽略）</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- 多返回值</span>
</span></span><span class="line"><span class="cl"><span class="kr">function</span> <span class="nf">maximun</span><span class="p">(</span><span class="n">a</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="kd">local</span> <span class="n">max_index</span> <span class="o">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">  <span class="kd">local</span> <span class="n">max_value</span> <span class="o">=</span> <span class="n">a</span><span class="p">[</span><span class="n">max_index</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">for</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="o">#</span><span class="n">a</span> <span class="kr">do</span>
</span></span><span class="line"><span class="cl">    <span class="kr">if</span> <span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">max_value</span> <span class="kr">then</span>
</span></span><span class="line"><span class="cl">      <span class="n">max_index</span> <span class="o">=</span> <span class="n">i</span>
</span></span><span class="line"><span class="cl">      <span class="n">max_value</span> <span class="o">=</span> <span class="n">a</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">    <span class="kr">end</span>
</span></span><span class="line"><span class="cl">  <span class="kr">end</span>
</span></span><span class="line"><span class="cl">  
</span></span><span class="line"><span class="cl">  <span class="kr">return</span> <span class="n">max_index</span><span class="p">,</span> <span class="n">max_value</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">j</span><span class="p">,</span> <span class="n">k</span> <span class="o">=</span> <span class="n">maximun</span><span class="p">({</span><span class="mi">6</span><span class="p">,</span> <span class="mi">9</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">15</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">10</span><span class="p">})</span> 
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="n">j</span><span class="p">,</span> <span class="n">k</span><span class="p">)</span> <span class="c1">-- 6    20</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- 可变长参数</span>
</span></span><span class="line"><span class="cl"><span class="kr">function</span> <span class="nf">add</span><span class="p">(...)</span>
</span></span><span class="line"><span class="cl">  <span class="kd">local</span> <span class="n">sum</span> <span class="o">=</span> <span class="mi">0</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">-- 使用 {...} 的方式可以将变长参数转换为 table</span>
</span></span><span class="line"><span class="cl">  <span class="kr">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">v</span> <span class="kr">in</span> <span class="n">ipairs</span><span class="p">({...})</span> <span class="kr">do</span>
</span></span><span class="line"><span class="cl">    <span class="n">sum</span> <span class="o">=</span> <span class="n">sum</span> <span class="o">+</span> <span class="n">v</span>
</span></span><span class="line"><span class="cl">  <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="kr">return</span> <span class="n">sum</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="n">add</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">))</span> <span class="c1">-- 6</span>
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="n">add</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">,</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">))</span> <span class="c1">-- 15</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h2 id="扩展内容">扩展内容<a hidden class="anchor" aria-hidden="true" href="#扩展内容">#</a></h2>
<h3 id="闭包">闭包<a hidden class="anchor" aria-hidden="true" href="#闭包">#</a></h3>
<p>同样与 JS 进行类比，上述对于 lua 函数的定义方式只是 lua 中提供的语法糖，本质上，函数的定义如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kr">function</span> <span class="nf">func</span><span class="p">()</span> <span class="n">print</span><span class="p">(</span><span class="s1">&#39;hello world&#39;</span><span class="p">)</span> <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- 等价于</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">func</span> <span class="o">=</span> <span class="kr">function</span><span class="p">()</span> <span class="n">print</span><span class="p">(</span><span class="s1">&#39;hello world&#39;</span><span class="p">)</span> <span class="kr">end</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>lua 函数的这种特性，可以将函数赋值给全局变量、局部变量或者表中的值。</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="n">a</span> <span class="o">=</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="n">a.p</span> <span class="o">=</span> <span class="n">print</span> <span class="c1">-- 将 print 函数赋值给表 a 的 p 变量</span>
</span></span><span class="line"><span class="cl"><span class="n">a.p</span><span class="p">(</span><span class="s1">&#39;hello world&#39;</span><span class="p">)</span> <span class="c1">-- 等同于 print(&#39;hello world&#39;)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- 非全局函数</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- 以下几种方式等价</span>
</span></span><span class="line"><span class="cl"><span class="n">foo</span> <span class="o">=</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="n">foo.bar</span> <span class="o">=</span> <span class="kr">function</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="kr">return</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">x</span> <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">foo</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="n">bar</span><span class="p">:</span> <span class="kr">function</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="kr">return</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">x</span> <span class="kr">end</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">foo</span> <span class="o">=</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="kr">function</span> <span class="nc">foo</span><span class="p">.</span><span class="nf">bar</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="kr">return</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">x</span> <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- 将函数声明为局部函数</span>
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="kr">function</span> <span class="nf">foo</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="kr">return</span> <span class="mi">2</span> <span class="o">*</span> <span class="n">x</span> <span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- 局部函数的声明问题</span>
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="kr">function</span> <span class="nf">fib</span><span class="p">(</span><span class="n">n</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="kr">if</span> <span class="n">n</span> <span class="o">&lt;=</span> <span class="mi">2</span> <span class="kr">then</span> <span class="kr">return</span> <span class="n">n</span> <span class="kr">end</span>
</span></span><span class="line"><span class="cl">  <span class="kr">return</span> <span class="n">fib</span><span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="n">fib</span><span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">2</span><span class="p">)</span> <span class="c1">-- 此处有问题，fib 此时不可见</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- 需要写成以下方式</span>
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">fib</span>
</span></span><span class="line"><span class="cl"><span class="n">fib</span> <span class="o">=</span> <span class="kr">function</span><span class="p">(</span><span class="n">n</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="kr">if</span> <span class="n">n</span> <span class="o">&lt;=</span> <span class="mi">2</span> <span class="kr">then</span> <span class="kr">return</span> <span class="n">n</span> <span class="kr">end</span>
</span></span><span class="line"><span class="cl">  <span class="kr">return</span> <span class="n">fib</span><span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="n">fib</span><span class="p">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">2</span><span class="p">)</span> <span class="c1">-- 解决上述问题</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>闭包是个比较难理解的概念，《Programming in Lua》中描述如下：</p>
<div class="details admonition quote open">
        <div class="details-summary admonition-title">
            <i class="icon fas fa-quote-right fa-fw"></i>Closure<i class="details-icon fas fa-angle-right fa-fw"></i>
        </div>
        <div class="details-content">
            <div class="admonition-content">Simply put, a closure is a function plus all it needs to access its upvalues correctly.</div>
        </div>
    </div>
<p>这里又要引入 upvalue（上值）的概念，示例如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kr">function</span> <span class="nf">newCounter</span><span class="p">()</span> 
</span></span><span class="line"><span class="cl">  <span class="kd">local</span> <span class="n">counter</span> <span class="o">=</span> <span class="mi">0</span> <span class="c1">-- 既不是局部变量，也不是全局变量，在 lua 中称之为 upvalue</span>
</span></span><span class="line"><span class="cl">  <span class="kr">return</span> <span class="kr">function</span><span class="p">()</span> 
</span></span><span class="line"><span class="cl">           <span class="n">counter</span> <span class="o">=</span> <span class="n">counter</span> <span class="o">+</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl">           <span class="kr">return</span> <span class="n">counter</span>
</span></span><span class="line"><span class="cl">         <span class="kr">end</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span> 
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">c1</span> <span class="o">=</span> <span class="n">newCounter</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="n">c1</span><span class="p">())</span> <span class="c1">-- 1</span>
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="n">c1</span><span class="p">())</span> <span class="c1">-- 2</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">c2</span> <span class="o">=</span> <span class="n">newCounter</span><span class="p">()</span> <span class="c1">-- 新增计数器，创建了新的环境，不同于 c1，因此重新计数</span>
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="n">c2</span><span class="p">())</span> <span class="c1">-- 1</span>
</span></span><span class="line"><span class="cl"><span class="n">print</span><span class="p">(</span><span class="n">c2</span><span class="p">())</span> <span class="c1">-- 2</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>上述代码中，<code>newCounter</code> 函数中定义了一个 <code>counter</code> 变量，它与普通局部变量最大的不同之处在于，当执行完 <code>c1 = newCounter()</code> 这句代码后，其实已经超出了 <code>counter</code> 的作用域，但是从后续 <code>c1()</code> 的两次调用中我们可以看到，仍然能够正常访问到 counter 的值，在 lua 中，类似 <code>counter</code> 的这种变量都被称为 upvalue（上值）。</p>
<p>闭包就是 <code>newCounter</code> 的匿名返回函数与该返回函数所在的一个上下文环境的统称（<code>counter</code> 就在该上下文环境中）。换句话说，实际上<code>newCounter</code> 返回的不仅仅是匿名函数本身，还包括了一个包含该匿名函数的上下文环境，环境中定义了一系列的 upvalue（若 <code>newCounter</code> 方法有参数，这些参数也属于 upvalue）。</p>
<p>闭包的应用场景较多，典型的一个应用是 GUI 交互的回调上，假设要做一个计算器的应用，需要 0-9 的数字按键，每个按键点击后需要在显示区域显示，因此可以定义如下函数：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kr">function</span> <span class="nf">digitButton</span> <span class="p">(</span><span class="n">digit</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="kr">return</span> <span class="n">Button</span><span class="p">{</span> <span class="n">label</span> <span class="o">=</span> <span class="n">digit</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">                  <span class="n">action</span> <span class="o">=</span> <span class="kr">function</span> <span class="p">()</span>
</span></span><span class="line"><span class="cl">                             <span class="n">add_to_display</span><span class="p">(</span><span class="n">digit</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">                           <span class="kr">end</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>其中，digit 即为按键上的数值，这样每个按钮对应的数值都不相同，通过使用闭包，可以将所有按钮的逻辑统一。</p>
<h3 id="metatable">metatable<a hidden class="anchor" aria-hidden="true" href="#metatable">#</a></h3>
<p><code>metatable</code> 提供了对 table 的操作重载功能，比如现在有两个 table 分别代表两个分数，如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="n">f1</span> <span class="o">=</span> <span class="p">{</span><span class="n">a</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="n">b</span> <span class="o">=</span> <span class="mi">2</span><span class="p">}</span> <span class="c1">-- 表示 1/2</span>
</span></span><span class="line"><span class="cl"><span class="n">f2</span> <span class="o">=</span> <span class="p">{</span><span class="n">a</span> <span class="o">=</span> <span class="mi">2</span><span class="p">,</span> <span class="n">b</span> <span class="o">=</span> <span class="mi">3</span><span class="p">}</span> <span class="c1">-- 表示 2/3</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>现在想要定义一个加法操作，表示两个分数相加的逻辑，即 <code>result = f1 + f2</code>，使用 metatalbe 如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="n">metafraction</span> <span class="o">=</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- __add 是 lua 规定的 metamethod 之一，表示 + 号的重载</span>
</span></span><span class="line"><span class="cl"><span class="kr">function</span> <span class="nc">metafraction</span><span class="p">.</span><span class="nf">__add</span><span class="p">(</span><span class="n">f1</span><span class="p">,</span> <span class="n">f2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">sum</span> <span class="o">=</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">  <span class="n">sum.b</span> <span class="o">=</span> <span class="n">f1.b</span> <span class="o">*</span> <span class="n">f2.b</span>
</span></span><span class="line"><span class="cl">  <span class="n">sum.a</span> <span class="o">=</span> <span class="n">f1.a</span> <span class="o">*</span> <span class="n">f2.b</span> <span class="o">+</span> <span class="n">f2.a</span> <span class="o">*</span> <span class="n">f1.b</span>
</span></span><span class="line"><span class="cl">  <span class="kr">return</span> <span class="n">sum</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- 设置 metatable，该 table 重载了 _add，为 f1 和 f2 提供了 + 号的重载操作</span>
</span></span><span class="line"><span class="cl"><span class="n">setmetatable</span><span class="p">(</span><span class="n">f1</span><span class="p">,</span> <span class="n">metafraction</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="n">setmetatable</span><span class="p">(</span><span class="n">f2</span><span class="p">,</span> <span class="n">metafraction</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl"><span class="c1">-- 直接使用 + 号进行分数的求和计算</span>
</span></span><span class="line"><span class="cl"><span class="n">s</span> <span class="o">=</span> <span class="n">f1</span> <span class="o">+</span> <span class="n">f2</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>除了 <code>_add</code>，lua 还提供了许多可供选择的操作：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="c1">-- __add(a, b)                     for a + b</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- __sub(a, b)                     for a - b</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- __mul(a, b)                     for a * b</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- __div(a, b)                     for a / b</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- __mod(a, b)                     for a % b</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- __pow(a, b)                     for a ^ b</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- __unm(a)                        for -a</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- __concat(a, b)                  for a .. b</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- __len(a)                        for #a</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- __eq(a, b)                      for a == b</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- __lt(a, b)                      for a &lt; b</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- __le(a, b)                      for a &lt;= b</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- __index(a, b)  &lt;fn or a table&gt;  for a.b</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- __newindex(a, b, c)             for a.b = c</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- __call(a, ...)                  for a(...)</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="模块化">模块化<a hidden class="anchor" aria-hidden="true" href="#模块化">#</a></h3>
<p>我们自定义一个 <code>mod.lua</code> 文件作为我们的模块，文件内容如下：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">M</span> <span class="o">=</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="kr">function</span> <span class="nf">sayMyName</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="n">print</span><span class="p">(</span><span class="s1">&#39;Hrunkner&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">function</span> <span class="nc">M</span><span class="p">.</span><span class="nf">sayHello</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="n">print</span><span class="p">(</span><span class="s1">&#39;Why hello there&#39;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="n">sayMyName</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">return</span> <span class="n">M</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p>如果需要在另一个文件中引入该模块，则需要使用 <code>require</code>：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">mod</span> <span class="o">=</span> <span class="n">require</span><span class="p">(</span><span class="s1">&#39;mod&#39;</span><span class="p">)</span> <span class="c1">-- 注意，没有.lua 后缀</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">mod.sayHello</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">mod.sayMyName</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- require 的作用可近似看为立即执行函数，但它有缓存机制，require 同一个模块只会执行一次</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">local</span> <span class="n">mod</span> <span class="o">=</span> <span class="p">(</span><span class="kr">function</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="c1">-- &lt;mod.lua 文件中的内容&gt;</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span><span class="p">)()</span>
</span></span></code></pre></td></tr></table>
</div>
</div><h3 id="面向对象">面向对象<a hidden class="anchor" aria-hidden="true" href="#面向对象">#</a></h3>
<p>lua 中没有提供内置的 Class 机制，但可以通过 table 和 metatable 实现面向对象的语义，其中 <code>__index</code> 的语义表示访问操作符 <code>.</code> 的重载（例如 table a 的 metatable 中设定了 __index 为 b，那么当使用 a.attr 访问 attr 时，会先在 table a 中查找，若 a 中没有，则会去 b 中查询 attr）：</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-lua" data-lang="lua"><span class="line"><span class="cl"><span class="c1">-- Dog 类本质上是一个 table</span>
</span></span><span class="line"><span class="cl"><span class="n">Dog</span> <span class="o">=</span> <span class="p">{}</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- function t:fn(...) 是一个语法糖，相当于 function t.fn(self, ...)，只是在函数中添加了一个 self 参数</span>
</span></span><span class="line"><span class="cl"><span class="kr">function</span> <span class="nc">Dog</span><span class="p">:</span><span class="nf">new</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="c1">-- 每次 new 生成一个新的 table</span>
</span></span><span class="line"><span class="cl">  <span class="n">newDog</span> <span class="o">=</span> <span class="p">{</span><span class="n">sound</span> <span class="o">=</span> <span class="s2">&#34;wang wang！&#34;</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="c1">-- 将第一个参数 self 的 __index 设定为 self 本身，表示 newDog 后续的查询范围是在本表内部</span>
</span></span><span class="line"><span class="cl">  <span class="n">self.__index</span> <span class="o">=</span> <span class="n">self</span>
</span></span><span class="line"><span class="cl">  <span class="c1">-- setmetatable 返回设定好 metatable 的 newDog</span>
</span></span><span class="line"><span class="cl">  <span class="kr">return</span> <span class="n">setmetatable</span><span class="p">(</span><span class="n">newDog</span><span class="p">,</span> <span class="n">self</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">function</span> <span class="nc">Dog</span><span class="p">:</span><span class="nf">makeSound</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="n">print</span><span class="p">(</span><span class="s1">&#39;I say&#39;</span> <span class="o">..</span> <span class="n">self.sound</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- 相当于 dogInstance = Dog.new(Dog)，相当于将新表 newDog 的 metatable 赋值为 Dog，并返回 newDog</span>
</span></span><span class="line"><span class="cl"><span class="n">dogInstance</span> <span class="o">=</span> <span class="n">Dog</span><span class="p">:</span><span class="n">new</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- 相当于 newDog.makesound(newDog)，此时，makeSound 存在于 Dog 表中，由于设定了 metatable 的 __index，所以 在 newDog 中能访问到，且传入的 self 为 newDog,即 self = newDog</span>
</span></span><span class="line"><span class="cl"><span class="n">dogInstance</span><span class="p">:</span><span class="n">makeSound</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- 类的继承</span>
</span></span><span class="line"><span class="cl"><span class="c1">-- LoudDog = Dog.new(Dog)，因此 self 为 Dog，即 LoudDog 的 metatable.__index 为 Dog</span>
</span></span><span class="line"><span class="cl"><span class="n">LoudDog</span> <span class="o">=</span> <span class="n">Dog</span><span class="p">:</span><span class="n">new</span><span class="p">()</span>                           
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- 重新定义 makeSound() 函数，注意这个函数此时是在 LoudDog 表中</span>
</span></span><span class="line"><span class="cl"><span class="kr">function</span> <span class="nc">LoudDog</span><span class="p">:</span><span class="nf">makeSound</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">  <span class="n">s</span> <span class="o">=</span> <span class="n">self.sound</span> <span class="o">..</span> <span class="s1">&#39; &#39;</span>                       
</span></span><span class="line"><span class="cl">  <span class="n">print</span><span class="p">(</span><span class="n">s</span> <span class="o">..</span> <span class="n">s</span> <span class="o">..</span> <span class="n">s</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="kr">end</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">-- LoudDog.new(LoudDog)，self 为 LoudDog，因此 seymour 的 metatable.__index 为 LoudDog</span>
</span></span><span class="line"><span class="cl"><span class="n">seymour</span> <span class="o">=</span> <span class="n">LoudDog</span><span class="p">:</span><span class="n">new</span><span class="p">()</span>                       
</span></span><span class="line"><span class="cl"><span class="c1">-- seymour.makeSound(seymour)，由于 seymour 的 metatable.__index 为 LoudDog，因此调用的是 LoudDog 中的 makeSound，实现了覆盖</span>
</span></span><span class="line"><span class="cl"><span class="n">seymour</span><span class="p">:</span><span class="n">makeSound</span><span class="p">()</span>  <span class="c1">-- &#39;woof woof woof&#39;      </span>
</span></span></code></pre></td></tr></table>
</div>
</div>

  </div>

  <footer class="post-footer">
    <ul class="post-tags">
      <li><a href="https://www.liyangjie.cn/tags/lua/">lua</a></li>
    </ul><div id="post-licensing" class="admonition warning">
    <div class="details-summary admonition-title">
        版权声明 <a rel="license" target="_blank" href="https://creativecommons.org/licenses/by/4.0/deed.zh"><i class="fab fa-creative-commons"></i> <i class="fab fa-creative-commons-by"></i></a>
    </div>
    
    <div class="details-content">
        <div class="admonition-content">
            <ul>
                <li>本文作者：SadBird。</li>
                <li>本文链接：https://www.liyangjie.cn/posts/work/hello-lua/。</li>
                <li>许可说明：本站所有文章除特殊声明外，均使用 <a href="https://creativecommons.org/licenses/by/4.0/deed.zh" rel="license" target="_blank"><i class="fab fa-creative-commons"></i> <i class="fab fa-creative-commons-by"></i> CC BY 4.0 </a> 许可协议，转载请注明出处。</li>
            </ul>
        </div>
    </div>
</div>
<nav class="paginav">
  <a class="prev" href="https://www.liyangjie.cn/posts/post/">
    <span class="title"><i class="fas fa-angle-double-left"></i> Prev Page</span>
    <br>
    <span>公式测试</span>
  </a>
  <a class="next" href="https://www.liyangjie.cn/posts/work/spring-circular-dependency/">
    <span class="title">Next Page <i class="fas fa-angle-double-right"></i></span>
    <br>
    <span>Spring 循环依赖</span>
  </a>
</nav>

  </footer>
<footer class="tc-container" id="comment">
    <div class="tc-title"><p class="c-title">Discussion</p></div>
    <div id="tcomments"></div>
</footer>
<script crossorigin="anonymous" src="/js/twikoo.min.64322d6748f9b8b12dfb029616065f8eeed16467444adde58bab7d98c5733adf.js" integrity="sha256-ZDItZ0j5uLEt&#43;wKWFgZfju7RZGdESt3li6t9mMVzOt8="></script>
<script>
    twikoo.init({
        envId: 'https://twikoo-nu-red.vercel.app/',
        el: '#tcomments',
        region: 'ap-shenzhen', 
        
        lang: 'zh-CN', 
    });
</script>
</article>
    </main>
    
<a href="#top" aria-label="go to top" title="Go to Top (Alt + G)" class="top-link" id="top-link" accesskey="g">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 6" fill="currentColor">
        <path d="M12 6H0l6-6z" />
    </svg>
</a><script>
    class Accordion {
        constructor(el) {
            
            this.el = el;
            
            this.summary = el.querySelector('summary');
            
            this.content = el.querySelector('.inner');

            
            this.animation = null;
            
            this.isClosing = false;
            
            this.isExpanding = false;
            
            this.summary.addEventListener('click', (e) => this.onClick(e));
        }

        onClick(e) {
            
            e.preventDefault();
            
            this.el.style.overflow = 'hidden';
            
            if (this.isClosing || !this.el.open) {
            this.open();
            
            } else if (this.isExpanding || this.el.open) {
            this.shrink();
            }
        }

        shrink() {
            
            this.isClosing = true;
            
            
            const startHeight = `${this.el.offsetHeight}px`;
            
            const endHeight = `${this.summary.offsetHeight}px`;
            
            
            if (this.animation) {
            
            this.animation.cancel();
            }
            
            
            this.animation = this.el.animate({
            
            height: [startHeight, endHeight]
            }, {
            duration: 400,
            easing: 'ease-out'
            });
            
            
            this.animation.onfinish = () => this.onAnimationFinish(false);
            
            this.animation.oncancel = () => this.isClosing = false;
        }

        open() {
            
            this.el.style.height = `${this.el.offsetHeight}px`;
            
            this.el.open = true;
            
            window.requestAnimationFrame(() => this.expand());
        }

        expand() {
            
            this.isExpanding = true;
            
            const startHeight = `${this.el.offsetHeight}px`;
            
            const endHeight = `${this.summary.offsetHeight + this.content.offsetHeight}px`;
            
            
            if (this.animation) {
            
            this.animation.cancel();
            }
            
            
            this.animation = this.el.animate({
            
            height: [startHeight, endHeight]
            }, {
            duration: 400,
            easing: 'ease-out'
            });
            
            this.animation.onfinish = () => this.onAnimationFinish(true);
            
            this.animation.oncancel = () => this.isExpanding = false;
        }

        onAnimationFinish(open) {
            
            this.el.open = open;
            
            this.animation = null;
            
            this.isClosing = false;
            this.isExpanding = false;
            
            this.el.style.height = this.el.style.overflow = '';
        }
    }

    document.querySelectorAll('.toc details').forEach((el) => {
        new Accordion(el);
    });
</script>
<a href="#comment" aria-label="go to bottom comment" title="Go to Bottom Comment (Alt + B)" class="top-link bottom-comment" id="bottom-comment" accesskey="b" style="visibility: visible; opacity: 1;">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 29.338 29.338" fill="currentcolor">
        <path d="M27.184,1.605H2.156C0.967,1.605,0,2.572,0,3.76v17.572c0,1.188,0.967,2.155,2.156,2.155h13.543
		l5.057,3.777c0.414,0.31,0.842,0.468,1.268,0.468c0.789,0,1.639-0.602,1.637-1.923v-2.322h3.523c1.188,0,2.154-0.967,2.154-2.155
		V3.76C29.338,2.572,28.371,1.605,27.184,1.605z M27.34,21.332c0,0.085-0.068,0.155-0.154,0.155h-5.523v3.955l-5.297-3.956H2.156
		c-0.086,0-0.154-0.07-0.154-0.155V3.759c0-0.085,0.068-0.155,0.154-0.155v0.001h25.029c0.086,0,0.154,0.07,0.154,0.155
		L27.34,21.332L27.34,21.332z M5.505,10.792h4.334v4.333H5.505C5.505,15.125,5.505,10.792,5.505,10.792z M12.505,10.792h4.334v4.333
		h-4.334V10.792z M19.505,10.792h4.334v4.333h-4.334V10.792z"/>
    </svg>
</a>

<script>
    let comment = document.getElementById("comment");
    let bottomToComment = document.getElementById("bottom-comment")
    
    document.addEventListener('scroll',function () {
        const viewPortHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight 
        const offsetTop = comment.offsetTop
        const scrollTop = document.documentElement.scrollTop
        const top = offsetTop - scrollTop
        if (top <= viewPortHeight + 100) {
            bottomToComment.style.visibility = "hidden";
            bottomToComment.style.opacity = "0";
        } else {
            bottomToComment.style.visibility = "visible";
            bottomToComment.style.opacity = "1";
        }
    })
</script><footer class="footer">
    <span>&copy; 2022 <a href="https://www.liyangjie.cn/">染竹君的个人博客</a></span>
    <span>
        Powered by
        <a href="https://gohugo.io/" rel="noopener noreferrer" target="_blank">Hugo</a> &
        <a href="https://git.io/hugopapermod" rel="noopener" target="_blank">PaperMod</a>
    </span>
    
</footer>

<script crossorigin="anonymous" src="/js/intersection-observer.min.c0ff09623e9f209a66c6130004e0422e1385fd8e8f8baaa14246a49c19827c68.js" integrity="sha256-wP8JYj6fIJpmxhMABOBCLhOF/Y6Pi6qhQkaknBmCfGg="></script>
<script crossorigin="anonymous" src="/js/lazyload.min.min.53e82bf9e8c145b953635b17fa3911ab6f3a8f1e4d37969aeb39d829d30fa6e9.js" integrity="sha256-U&#43;gr&#43;ejBRblTY1sX&#43;jkRq286jx5NN5aa6znYKdMPpuk="></script>


<script crossorigin="anonymous" src="/js/lightgallery.min.5d1410b8c831852c6c62b2dfad75f4e0f046a13e1826f97cd7a742523fab608c.js" integrity="sha256-XRQQuMgxhSxsYrLfrXX04PBGoT4YJvl816dCUj&#43;rYIw="></script>
<script crossorigin="anonymous" src="/js/lg-thumbnail.min.afa3995936244c14f68b85b13d657c368dc787cbcea863179950ac494dab6117.js" integrity="sha256-r6OZWTYkTBT2i4WxPWV8No3Hh8vOqGMXmVCsSU2rYRc="></script>
<script crossorigin="anonymous" src="/js/lg-zoom.min.a47b38d6f7138dce5f712f77780f339b5ec26af3bcd44950bcf736754ec88ed4.js" integrity="sha256-pHs41vcTjc5fcS93eA8zm17CavO81ElQvPc2dU7IjtQ="></script>
<script>
    const lazyLoadInstance = new LazyLoad({
        
    }); 
    
    const config = {
        selector: ".lightgallery",
        mode: "lg-slide",
        plugins: [lgZoom, lgThumbnail],
        speed: 400,
        hideBarsDelay: 2000,
        mousewheel: true,
        thumbnail: true,
        exThumbImage: "data-thumbnail",
        thumbWidth: 80,
        thumbContHeight: 80,
        mobileSettings: { controls: true, showCloseIcon: true, download: true, },
        licenseKey: "28AC9E09-3D8C45D8-8D6124E0-8FF74FF3", 
    };

    lightGallery(document.getElementsByClassName('post-content')[0], config);
</script>
<script>
    let details = document.getElementsByClassName('details')
    details = details || [];
    for (let i = 0; i < details.length; i++) {
        let element = details[i]
        const summary = element.getElementsByClassName('details-summary')[0];
        if (summary) {
            summary.addEventListener('click', () => {
                element.classList.toggle('open');
            }, false);
        }
    }
</script>

<script>
    let menu = document.getElementById('menu')
    if (menu) {
        menu.scrollLeft = localStorage.getItem("menu-scroll-position");
        menu.onscroll = function () {
            localStorage.setItem("menu-scroll-position", menu.scrollLeft);
        }
    }

    document.querySelectorAll('a[href^="#"]').forEach(anchor => {
        anchor.addEventListener("click", function (e) {
            e.preventDefault();
            var id = this.getAttribute("href").substr(1);
            if (!window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
                document.querySelector(`[id='${decodeURIComponent(id)}']`).scrollIntoView({
                    behavior: "smooth"
                });
            } else {
                document.querySelector(`[id='${decodeURIComponent(id)}']`).scrollIntoView();
            }
            if (id === "top") {
                history.replaceState(null, null, " ");
            } else {
                history.pushState(null, null, `#${id}`);
            }
        });
    });

</script>
<script>
    var mybutton = document.getElementById("top-link");
    window.onscroll = function () {
        if (document.body.scrollTop > 800 || document.documentElement.scrollTop > 800) {
            mybutton.style.visibility = "visible";
            mybutton.style.opacity = "1";
        } else {
            mybutton.style.visibility = "hidden";
            mybutton.style.opacity = "0";
        }
    };

</script>
<script>
    document.getElementById("theme-toggle").addEventListener("click", () => {
        if (document.body.className.includes("dark")) {
            document.body.classList.remove('dark');
            localStorage.setItem("pref-theme", 'light');
        } else {
            document.body.classList.add('dark');
            localStorage.setItem("pref-theme", 'dark');
        }
    })

</script>
<script>
    document.querySelectorAll('pre > code').forEach((codeblock) => {
        const container = codeblock.parentNode.parentNode;

        const copybutton = document.createElement('button');
        copybutton.classList.add('copy-code');
        copybutton.innerHTML = 'copy';

        function copyingDone() {
            copybutton.innerHTML = 'copied!';
            setTimeout(() => {
                copybutton.innerHTML = 'copy';
            }, 2000);
        }

        copybutton.addEventListener('click', (cb) => {
            if ('clipboard' in navigator) {
                navigator.clipboard.writeText(codeblock.textContent);
                copyingDone();
                return;
            }

            const range = document.createRange();
            range.selectNodeContents(codeblock);
            const selection = window.getSelection();
            selection.removeAllRanges();
            selection.addRange(range);
            try {
                document.execCommand('copy');
                copyingDone();
            } catch (e) { };
            selection.removeRange(range);
        });

        if (container.classList.contains("highlight")) {
            container.appendChild(copybutton);
        } else if (container.parentNode.firstChild == container) {
            
        } else if (codeblock.parentNode.parentNode.parentNode.parentNode.parentNode.nodeName == "TABLE") {
            
            codeblock.parentNode.parentNode.parentNode.parentNode.parentNode.appendChild(copybutton);
        } else {
            
            codeblock.parentNode.appendChild(copybutton);
        }
    });
</script>


<script type="text/x-mathjax-config">
    MathJax.Hub.Config({
      tex2jax: {
        inlineMath: [['$','$'], ['\\(','\\)']],
        processEscapes: true
      }
    });
</script>

<script type="text/javascript"
  src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML">
</script></body>

</html>